using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Drawing;

namespace AlgorithmsVisualized.Controls
{
    public class GraphPictureBox : PictureBox
    {
        #region DataMembers
        private List<ShapeBase> _shapes = new List<ShapeBase>();
        private ContextMenuStrip mnuNode;
        private System.ComponentModel.IContainer components;
        private ToolStripMenuItem startNode_mnuNode;
        //private List<Type> _selectableTypes;
        private ShapeBase _selectedShape = null;
        private List<Node> _startNodes = new List<Node>();
        #endregion
        #region Events
        public event EdgeHandler NewEdgeRequest;
        public event ShapeHandler SelectionChanged;
        public event ShapeHandler StartNodeChanged;
        #endregion
        #region Properties
        public ShapeBase SelectedShape
        {
            get { return _selectedShape; }
            set { _selectedShape = value; }
        }
        public ReadOnlyCollection<ShapeBase> Shapes
        {
            get { return _shapes.AsReadOnly(); }
        }
        public ReadOnlyCollection<Edge> Edges
        {
            get
            {
                List<Edge> edges = new List<Edge>();
                foreach (ShapeBase shape in Shapes)
                {
                    if (shape is Edge)
                        edges.Add(shape as Edge);
                }
                return edges.AsReadOnly();
            }
        }
        public ReadOnlyCollection<Node> Nodes
        {
            get
            {
                List<Node> nodes = new List<Node>();
                foreach (ShapeBase shape in Shapes)
                {
                    if (shape is Node)
                        nodes.Add(shape as Node);
                }
                return nodes.AsReadOnly();
            }
        }
        public List<Node> StartNodes
        {
            get { return _startNodes; }
            set { _startNodes = value; }
        }
        #endregion
        public GraphPictureBox()
        {
            mnuNode = new ContextMenuStrip();
            startNode_mnuNode = new ToolStripMenuItem();
            startNode_mnuNode.Text = "Set as Start Node";
            mnuNode.Items.Add(startNode_mnuNode);
            startNode_mnuNode.Click += new EventHandler(startNode_mnuNode_Click);
        }
        #region Event Handlers
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (SelectedShape != null)
                    SelectedShape.Deselect();
                List<ShapeBase> NewShapes = new List<ShapeBase>();
                //ShapeBase shape = GetShapeFromLocation(e.Location);
                //if (shape != null)
                foreach (ShapeBase shape in _shapes)
                {
                    if (shape.IsInside(e.Location))
                    {
                        shape.OnMouseUp(e);
                        if (ModifierKeys == Keys.Shift)
                        {
                            if (shape == SelectedShape)
                                continue;
                            EdgeArgs args = new EdgeArgs((Node)SelectedShape, (Node)shape);
                            OnNewEdgeRequest(ref args);
                            Edge edge = Edge.CreateDefault((Node)SelectedShape, (Node)shape);
                            ((Node)SelectedShape).Adj.Add(edge);
                            edge.IsDirected = args.IsDirected;
                            edge.Weight = args.Weight;
                            NewShapes.Add(edge);
                            SubscribeShapeEvents(edge);
                            Invalidate();
                        }
                        if (SelectedShape != null && shape != SelectedShape)
                            SelectedShape.Deselect();
                        SelectedShape = shape;
                        shape.Select();
                        OnSelectionChanged(new ShapeArgs(shape));
                    }
                }
                _shapes.AddRange(NewShapes);
            }
            else if (e.Button == MouseButtons.Right)
            {
                ShapeBase shape = GetShapeFromLocation(e.Location);
                if (shape != null)
                {
                    mnuNode.Tag = shape;
                    CreateNodeMenu(shape);
                    mnuNode.Show(this, e.Location);
                }
            }

            base.OnMouseUp(e);
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (SelectedShape != null)
                SelectedShape.OnMouseMove(e);
            /*
            foreach (ShapeBase shape in _shapes)
            {
                if (shape.IsInside(e.Location))
                    shape.OnMouseMove(e);
            }
             */
            base.OnMouseMove(e);
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                foreach (ShapeBase shape in _shapes)
                {
                    if (shape.IsInside(e.Location))
                    {
                        if (ModifierKeys == Keys.Shift)
                            shape.OnKeyDown(new KeyEventArgs(Keys.Shift));
                        shape.OnMouseDown(e);
                        shape.OnKeyUp(new KeyEventArgs(Keys.Shift));

                        if (SelectedShape != null && shape != SelectedShape)
                            SelectedShape.Deselect();
                        SelectedShape = shape;
                        shape.Select();
                        OnSelectionChanged(new ShapeArgs(shape));
                    }
                }
            }
            base.OnMouseDown(e);
        }
        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Node n = Node.CreateDefault(new Point(e.X, e.Y));
                SubscribeShapeEvents(n);
                if (SelectedShape != null)
                    SelectedShape.Deselect();
                n.Select();
                OnSelectionChanged(new ShapeArgs(n));
                SelectedShape = n;
                _shapes.Add(n);
                Invalidate();
            }
            base.OnMouseDoubleClick(e);
            this.Focus();
        }

        protected virtual void OnNewEdgeRequest(ref EdgeArgs args)
        {
            if (NewEdgeRequest != null)
                NewEdgeRequest(this, ref args);
        }
        protected virtual void OnSelectionChanged(ShapeArgs args)
        {
            if (SelectionChanged != null)
                SelectionChanged(this, args);
        }
        protected override void OnPaint(PaintEventArgs pe)
        {
            Bitmap bit = new Bitmap(Width, Height);
            Graphics g = Graphics.FromImage(bit);
            PaintEventArgs ev = new PaintEventArgs(g, pe.ClipRectangle);
            foreach (ShapeBase shape in _shapes)
                shape.OnPaint(ev);
            Graphics orig = pe.Graphics;
            orig.Clear(BackColor);
            orig.DrawImageUnscaled(bit, 0, 0);
        }
        protected virtual void OnStartNodeChanged(ShapeArgs args)
        {
            if (StartNodeChanged != null)
                StartNodeChanged(this, args);
        }
        void startNode_mnuNode_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem strip = sender as ToolStripMenuItem;
            if (strip == null)
                return;
            Node n = strip.Owner.Tag as Node;
            
            if (strip.Checked)
            {
                n.RemoveBold();
                StartNodes.Remove(n);
                OnStartNodeChanged(new ShapeArgs(n));
            }
            else
            {
                if (StartNodes.Contains(n))
                    return;
                n.MakeBold();
                StartNodes.Add(n);
                OnStartNodeChanged(new ShapeArgs(n));
            }
            strip.Checked = !strip.Checked;
            Invalidate();
        }
        #endregion
        private void SubscribeShapeEvents(ShapeBase shape)
        {
            shape.RedrawRequest += shape_RedrawRequest;
        }
        void shape_RedrawRequest(object sender, EventArgs e)
        {
            Invalidate();
        }
        private ShapeBase GetShapeFromLocation(Point location)
        {
            foreach (ShapeBase shape in _shapes)
                if (shape.IsInside(location))
                    return shape;
            return null;
        }

        private void CreateNodeMenu(ShapeBase shape)
        {
            startNode_mnuNode.Checked = shape.IsBold;
        }
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.mnuNode = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.startNode_mnuNode = new System.Windows.Forms.ToolStripMenuItem();
            this.mnuNode.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
            this.SuspendLayout();
            // 
            // mnuNode
            // 
            this.mnuNode.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.startNode_mnuNode});
            this.mnuNode.Name = "mnuNode";
            this.mnuNode.Size = new System.Drawing.Size(160, 26);
            // 
            // startNode_mnuNode
            // 
            this.startNode_mnuNode.Name = "startNode_mnuNode";
            this.startNode_mnuNode.Size = new System.Drawing.Size(159, 22);
            this.startNode_mnuNode.Text = "Set as Start Node";
            this.mnuNode.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this)).EndInit();
            this.ResumeLayout(false);

        }
    }
    public delegate void EdgeHandler(object sender, ref EdgeArgs args);
    public delegate void ShapeHandler(object sender, ShapeArgs args);
    public class ShapeArgs : EventArgs
    {
        private ShapeBase _shape;

        public ShapeBase Shape
        {
            get { return _shape; }
        }
        public ShapeArgs(ShapeBase shape)
        {
            _shape = shape;
        }
    }
    public class EdgeArgs : EventArgs
    {
        private Node _from;
        private Node _to;
        private bool _isDirected = false;
        private double _weight = 0.0;

        public double Weight
        {
            get { return _weight; }
            set { _weight = value; }
        }
        public bool IsDirected
        {
            get { return _isDirected; }
            set { _isDirected = value; }
        }
        public Node To
        {
            get { return _to; }
        }
        public Node From
        {
            get { return _from; }
        }
        public EdgeArgs(Node from, Node to)
        {
            _from = from;
            _to = to;
        }
    }
}
